#include "Dlist_t.h"
#include "DlistIterator_t.h"

template<class T>
Dlist_t<T>::Dlist_t()
	: m_head(NULL)
	, m_curr(NULL)
	, m_tail(NULL)
{
	// Create head
	m_head = new DlistElem_t<T>(DlistElem_t<T>::HEAD);

	// Create tail
	m_tail = new DlistElem_t<T>(DlistElem_t<T>::TAIL);

	// Initialize
	initialize();
}

template<class T>
Dlist_t<T>::~Dlist_t()
{
	// Remove all elements
	removeAll();

	// Delete head
	delete m_head;

	// Delete tail
	delete m_tail;
}

template<class T>
Dlist_t<T>::Dlist_t(const Dlist_t& other)
{
	// Create head
	m_head = new DlistElem_t<T>(DlistElem_t<T>::HEAD);

	// Create tail
	m_tail = new DlistElem_t<T>(DlistElem_t<T>::TAIL);

	// Initialize
	initialize();

	// Iterate over list
	DlistElem_t<T>* elem = other.m_head->next();
	while(elem->type() != DlistElem_t<T>::TAIL)
	{
		// Insert new elem
		insert(elem->get());

		// Get next list elem
		elem = elem->next();
	}

	if(count() != other.count())
		throw "Dlist_t: copy ctor error";
}

template<class T>
Dlist_t<T>& Dlist_t<T>::operator=(const Dlist_t& other)
{
	if(this != &other)
	{
		// Remove all
		removeAll();

		// Initialize
		initialize();

		// Iterate over list
		DlistElem_t<T>* elem = other.m_head->next();
		while(elem->type() != DlistElem_t<T>::TAIL)
		{
			// Insert new elem
			insert(elem->get());

			// Get next list elem
			elem = elem->next();
		}

		if(count() != other.count())
			throw "Dlist_t: operator= error";
	}

	return *this;
}

template<class T>
T* Dlist_t<T>::next()
{
	if(m_curr->next() == NULL)
		return NULL;

	m_curr = m_curr->next();

	if(m_curr->type() == DlistElem_t<T>::TAIL)
		return NULL;

	return m_curr->get();
}

template<class T>
T* Dlist_t<T>::prev()
{
	if(m_curr->prev() == NULL)
		return NULL;

	m_curr = m_curr->prev();

	if(m_curr->type() == DlistElem_t<T>::HEAD)
		return NULL;

	return m_curr->get();
}

template<class T>
void Dlist_t<T>::reset()
{
	// Set curr to list head
	m_curr = m_head;
}

template<class T>
void Dlist_t<T>::doAppend(T* const elem, int index)
{
	// Create new list elem
	DlistElem_t<T>* listElem = new DlistElem_t<T>(elem);

	DlistElem_t<T>* next = NULL;
	DlistElem_t<T>* prev = NULL;

	if(index == -1) // Insert to front
	{
		next = m_head->next();
		prev = m_head;
	}
	else if(index == count() - 1) // Insert to back
	{
		next = m_tail;
		prev = m_tail->prev();
	}
	else // Insert to middle
	{
		DlistElem_t<T>* temp = lookupIndex(index); // temp != NULL
		next = temp->next();
		prev = temp;
	}

	// Set next and prev
	listElem->setNext(next);
	listElem->setPrev(prev);

	// Update prev
	prev->setNext(listElem);

	// Update next
	next->setPrev(listElem);

	// Update inner iterator
	m_curr = listElem;
}

template<class T>
void Dlist_t<T>::doRemove(const T& value)
{
	// Lookup value
	DlistElem_t<T>* listElem = lookup(value);
	if(listElem == NULL)
		return;

	// Update next and prev elements to point at each other
	listElem->prev()->setNext(listElem->next());
	listElem->next()->setPrev(listElem->prev());

	// Delete list elem
	delete listElem;

	// Reset
	reset();
}

template<class T>
void Dlist_t<T>::doRemoveAll()
{
	// Iterate over list
	DlistElem_t<T>* elem = m_head->next();
	while(elem->type() != DlistElem_t<T>::TAIL)
	{
		// Save current list elem
		DlistElem_t<T>* temp = elem;

		// Get next list elem
		elem = elem->next();

		// Delete current list elem
		delete temp;
	}

	// Initialize
	initialize();
}

template<class T>
void Dlist_t<T>::doRemoveAndDeleteAll()
{
	// Iterate over list
	DlistElem_t<T>* elem = m_head->next();
	while(elem->type() != DlistElem_t<T>::TAIL)
	{
		// Save current list elem
		DlistElem_t<T>* temp = elem;

		// Get next list elem
		elem = elem->next();

		// Delete current list elem data
		delete temp->get();

		// Delete current list elem
		delete temp;
	}

	// Initialize
	initialize();
}

template<class T>
Iterator_t<T>* Dlist_t<T>::iterator() const
{
	return new DlistIterator_t<T>(*this);
}

template<class T>
DlistElem_t<T>* Dlist_t<T>::lookup(const T& value) const
{
	// Iterate over list
	DlistElem_t<T>* curr = m_head->next();
	while(curr->type() != DlistElem_t<T>::TAIL)
	{
		if(*curr->get() == value)
			return curr;

		curr = curr->next();
	}

	// No elem found
	return NULL;
}

template<class T>
DlistElem_t<T>* Dlist_t<T>::lookupIndex(size_t index) const
{
	// Iterate over list
	DlistElem_t<T>* curr = m_head->next();
	for(size_t i = 0; curr->type() != DlistElem_t<T>::TAIL; i++)
	{
		if(i == index)
			return curr;

		curr = curr->next();
	}

	// No elem found
	return NULL;
}

template<class T>
void Dlist_t<T>::initialize()
{
	// Set head and tail elements to point at each other
	m_head->setNext(m_tail);
	m_tail->setPrev(m_head);

	// Reset
	reset();
}